package com.derbysoft.lowcostadapter.dswitch3.service.impl;

import com.derbysoft.dswitch.dto.common.KeyValue;
import com.derbysoft.dswitch.dto.hotel.reservation.CancelInfoDTO;
import com.derbysoft.dswitch.dto.hotel.reservation.HotelCancelRS;
import com.derbysoft.dswitch.remote.hotel.dto.ErrorDTO;
import com.derbysoft.dswitch.remote.hotel.dto.HotelCancelRequest;
import com.derbysoft.dswitch.remote.hotel.dto.HotelCancelResponse;
import com.derbysoft.lowcostadapter.commons.dto.*;
import com.derbysoft.lowcostadapter.commons.exception.IdentificationNotFoundException;
import com.derbysoft.lowcostadapter.commons.exception.NetErrorException;
import com.derbysoft.lowcostadapter.commons.exception.ReservationNotFound;
import com.derbysoft.lowcostadapter.commons.model.ProviderIdentification;
import com.derbysoft.lowcostadapter.commons.model.Reservation;
import com.derbysoft.lowcostadapter.commons.redis.service.ProviderIdentificationCacheService;
import com.derbysoft.lowcostadapter.commons.repository.ProviderIdentificationRepository;
import com.derbysoft.lowcostadapter.commons.repository.ReservationRepository;
import com.derbysoft.lowcostadapter.commons.utils.ErrorMessageUtils;
import com.derbysoft.lowcostadapter.dswitch3.handler.impl.ElapsedTimeType;
import com.derbysoft.lowcostadapter.dswitch3.service.HotelProviderService;
import com.derbysoft.lowcostadapter.webservice.LowCostWebService;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

/**
 * Created by: jason
 * Date: 2012-05-17
 */
@Component("ds3HotelCancelService")
public class HotelCancelServiceImpl extends BaseHotelService
        implements HotelProviderService<HotelCancelRequest, HotelCancelResponse> {
    private final Log logger = LogFactory.getLog(HotelCancelServiceImpl.class);

    @Autowired
    private ReservationRepository reservationRepository;

    @Autowired
    @Qualifier("reservationWebService")
    private LowCostWebService reservationWebService;

    @Autowired
    protected ProviderIdentificationCacheService providerIdentificationCacheService;

    @Autowired
    protected ProviderIdentificationRepository providerIdentificationRepository;

    @Override
    public HotelCancelResponse execute(HotelCancelRequest cancelRequest) {
        HotelCancelResponse hotelCancelResponse = new HotelCancelResponse();
        hotelCancelResponse.setTaskId(cancelRequest.getHeader().getTaskId());

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try {
            Reservation reservation = reservationRepository.findByErsp(cancelRequest.getHotelCancelRQ().getErsp());
            if (reservation == null) {
                throw new ReservationNotFound(cancelRequest.getHotelCancelRQ().getErsp());
            }
            Cancel cancel = translateRequest(reservation.getBookingReference(), cancelRequest.getHeader().getSource());
            CancelResponse cancelResponse = (CancelResponse) reservationWebService.send(cancel);
            ReturnStatus returnStatus = cancelResponse.getCancelResult().getReturnStatus();
            if (!returnStatus.isSuccess() || StringUtils.isNotBlank(returnStatus.getException())) {
                if (stopWatch.isRunning()) {
                    stopWatch.stop();
                }
                String webserviceTime = String.valueOf(stopWatch.getTotalTimeMillis());
                hotelCancelResponse.setError(createErrorDTO(cancelResponse));
                hotelCancelResponse.getElapsedTimes().add(new KeyValue(ElapsedTimeType.LCB_CANCEL.name(), webserviceTime));
                return hotelCancelResponse;
            }

            if (stopWatch.isRunning()) {
                stopWatch.stop();
            }
            long webserviceTime = stopWatch.getTotalTimeMillis();

            saveCancelledNumber(cancelRequest.getHotelCancelRQ().getErsp(), cancelResponse);

            HotelCancelRS hotelCancelRS = translateResponse(cancelResponse);
            hotelCancelResponse.setHotelCancelRS(hotelCancelRS);
            hotelCancelResponse.getElapsedTimes().add(new KeyValue(ElapsedTimeType.LCB_CANCEL.name(), String.valueOf(webserviceTime)));
            return hotelCancelResponse;
        } catch (Exception ex) {
            logger.error(ErrorMessageUtils.buildErrorMessage(ex));
            if (stopWatch.isRunning()) {
                stopWatch.stop();
            }
            String webserviceTime = String.valueOf(stopWatch.getTotalTimeMillis());
            hotelCancelResponse.setError(createErrorDTO(ex));
            hotelCancelResponse.getElapsedTimes().add(new KeyValue(ElapsedTimeType.LCB_CANCEL.name(), String.valueOf(webserviceTime)));
            return hotelCancelResponse;
        }
    }

    private HotelCancelRS translateResponse(CancelResponse cancelResponse) {
        HotelCancelRS hotelCancelRS = new HotelCancelRS();
        CancelInfoDTO cancelInfo = new CancelInfoDTO();
        cancelInfo.setHotelCancellationNO(cancelResponse.getCancelResult().getCancellationReference());
        hotelCancelRS.setCancelInfo(cancelInfo);
        return hotelCancelRS;
    }

    private void saveCancelledNumber(String ersp, CancelResponse cancelResponse) {
        String cancelNumber = cancelResponse.getCancelResult().getCancellationReference();
        if (cancelNumber != null) {
            reservationRepository.cancel(ersp, cancelNumber);
        }
    }

    private Cancel translateRequest(String confirmNumber, String channelCode) {
        Cancel cancel = new Cancel();
        CancelRequest cancelRequest = new CancelRequest();
        cancelRequest.setLoginDetails(createLoginDetails(channelCode));
        cancelRequest.setBookingReference(confirmNumber);
        cancel.setCancelRequest(cancelRequest);
        return cancel;
    }

    protected LoginDetails createLoginDetails(String channelCode) {
        LoginDetails loginDetails = new LoginDetails();
        ProviderIdentification identification = loadByChannel(channelCode);
        loginDetails.setLogin(identification.getUserName());
        loginDetails.setPassword(identification.getPassWd());
        loginDetails.setAgentReference(identification.getAgentReference());
        return loginDetails;
    }

    protected ProviderIdentification loadByChannel(String channelCode) {
        ProviderIdentification identification = providerIdentificationCacheService.get(channelCode);
        if (identification == null) {
            identification = providerIdentificationRepository.loadByChannel(channelCode);
        }
        if (identification == null) {
            throw new IdentificationNotFoundException(channelCode);
        }
        return identification;
    }

    private ErrorDTO createErrorDTO(Exception ex) {
        ErrorDTO errorDTO = new ErrorDTO();
        errorDTO.setCode(SYSTEM);
        errorDTO.setSource(ex instanceof NetErrorException ? provider : adapter);
        errorDTO.setMessage(ErrorMessageUtils.buildErrorMessage(ex));
        return errorDTO;
    }

    private ErrorDTO createErrorDTO(CancelResponse cancelResponse) {
        ErrorDTO errorDTO = new ErrorDTO();
        errorDTO.setCode(SYSTEM);
        errorDTO.setSource(provider);
        errorDTO.setMessage(cancelResponse.getCancelResult().getReturnStatus().getException());
        return errorDTO;
    }
}
